
#include "sock.hpp"
#include <functional>
#include <cstring>
#include <string>
#include <sys/epoll.h>
#include <cerrno>


namespace my_epoll
{
static const int defaultport=8080;
static const int defaultValue=-1;
static const int size=128;
static const int defaultnum=64;

using func_t=std::function<std::string (const std::string&)>;

enum{EPOLL_CREATE_ERR};

class EpollServer{

    public:
        EpollServer(func_t func,int port=defaultport):_port(port),_listensock(-1),_epfd(defaultValue),_func(func),_num(defaultnum),_revs(nullptr){}

        void initServer()
        {
            //1. 创建socket
            _listensock=sock::Socket();
            sock::Bind(_listensock,_port);    
            sock::Listen(_listensock);
            //2. 创建epoll模型
            _epfd=epoll_create(size);
            if(_epfd<0)
            {
                logMessage(FATAL,"epoll create error: %s ",strerror(errno));
                exit(EPOLL_CREATE_ERR);
            }
            //3. 添加listensock到epoll中
            struct epoll_event ev;
            ev.events=EPOLLIN;
            ev.data.fd=_listensock;
            epoll_ctl(_epfd,EPOLL_CTL_ADD,_listensock,&ev);

            //4. 申请就绪事件空间
            _revs=new struct epoll_event[_num];

        }

        void HandlerEvent(int readyNum)
        {
            for(int i=0;i<readyNum;++i)
            {
                uint32_t events=_revs[i].events;
                int sock=_revs[i].data.fd;

                if(sock==_listensock && (events & EPOLLIN))
                {
                    //_listensock读事件就绪，获取新连接
                    std::string clientip;
                    uint16_t clientport;
                    int fd=sock::Accept(sock,&clientip,&clientport);
                    if(fd<0)
                    {
                        logMessage(WARNING,"accept error ");
                        continue;
                    }
                    //获取fd成功，并不可以直接读取
                    struct epoll_event ev;
                    ev.events=EPOLLIN;
                    ev.data.fd=fd;
                    epoll_ctl(_epfd,EPOLL_CTL_ADD,fd,&ev);
                }
                else if(events & EPOLLIN)
                {
                    //普通读取事件
                    //这里的读取依旧有问题
                    char buffer[1024];
                    int n=recv(sock,buffer,sizeof(buffer)-1,0);
                    if(n>0)
                    {
                        buffer[n]=0;
                        logMessage(DEBUG,"client# %s",buffer);
                        //TODO
                        std::string respone =_func(buffer);
                        send(sock,respone.c_str(),respone.size(),0);
                    }
                    else if(n==0)
                    {
                        //建议先从epoll移除，才close fd
                        epoll_ctl(_epfd,EPOLL_CTL_DEL,sock,nullptr);
                        close(sock);
                        logMessage(NORMAL,"client quit");
                    }
                    else
                    {
                        epoll_ctl(_epfd,EPOLL_CTL_DEL,sock,nullptr);
                        close(sock);
                        logMessage(ERROR,"recv error code: %d  codestring: %s",errno, strerror(errno));
                    }

                }
                else
                {

                }
            }
        }

        void start()
        {
            int timeout=-1;
            for(;;){
                int n=epoll_wait(_epfd,_revs,_num,timeout);
                switch(n)
                {
                    case 0:
                        logMessage(NORMAL,"timeout.....");
                        break;
                    case -1:
                        logMessage(WARNING,"epoll_wait failed, code %d ,errstring: %s ",errno,strerror(errno) );
                        break;
                    default:
                        logMessage(NORMAL,"have event ready");
                        HandlerEvent(n);
                        break;
                }
            }
        }

        ~EpollServer()
        {
            if(_listensock>0)
                close(_listensock);
            if(_epfd!=defaultValue)
                close(_epfd);
            if(_revs)
                delete[] _revs;
        }
    private:
        int _port;
        int _listensock;
        int _epfd;
        struct epoll_event* _revs;
        int _num;
        func_t _func;
};

}

